package com.ctsi.framework.mybatis.support;

import com.ctsi.commons.util.UtilValidate;
import com.ctsi.commons.util.response.Page;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.ibatis.session.RowBounds;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: zhangjw
 * @Date: 2018/11/15 12:09
 * @Description: 直接注入使用
 */
public class MybatisGenericDao  extends SqlSessionDaoSupport{

	private static final Logger logger = LoggerFactory.getLogger(MybatisGenericDao.class);
	
	
	public static final String POSTFIX_INSERT = ".insert";

	public static final String POSTFIX_INSERTCOLLECT = ".insertCollect";
	
	public static final String POSTFIX_UPDATE = ".update";

	public static final String POSTFIX_DELETE = ".delete";
	
	public static final String POSTFIX_DELETE_PRIAMARYKEY = ".deleteByPrimaryKey";

	public static final String POSTFIX_SELECT = ".select";

	public static final String POSTFIX_SELECTMAP = ".selectByMap";

	public static final String POSTFIX_SELECTSQL = ".selectBySql";

	public static final String POSTFIX_STA_COUNT = "_count";
	
	public MybatisGenericDao(){}
	/**
	 * 根据ID获取对象
	 * 默认查找 ID为：select 的查询语句
	 * 注意SQL语句获取参数时的写法
	 * 如果传递的参数是个基本类型参数时
	 * 获取值为均_parameter
	 * 否则抛出：There is no getter for property named "XXXX" in class java.lang.String
	 * 错误
	 * @param entityClass
	 * @param id
	 * @return
	 */
	public <T> T get(Class<T> entityClass, Serializable id) {
		try {
			return getSqlSession().selectOne(entityClass.getName() + POSTFIX_SELECT, id);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
//		return getSqlSession().selectOne(entityClass.getName() + POSTFIX_SELECT, id);
	}
	/**
	 * 获得全部对象
	 * @param entityClass
	 * @return
	 */
	public <T> List<T> getAll(Class<T> entityClass) {
		try {
			return getSqlSession().selectList(entityClass.getName() + POSTFIX_SELECT);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	/**
	 * 简单的添加
	 * 默认执行ID为： insert的添加语句
	 * 注意实体对象对应的表中主键字段的属性值不能为空，否则保存失败
	 * @param o
	 * @return
	 */
	public int insert(Object o) {
		try {
			return getSqlSession().insert(o.getClass().getName() + POSTFIX_INSERT, o);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
/**
 * 添加收藏
 */
public int insertCollect(Object o) {
	try {
		return getSqlSession().insert(o.getClass().getName() + POSTFIX_INSERTCOLLECT, o);
	} catch (Exception e) {
		logger.error("{}",e);
		throw new SQLExecuteException(e);
	}
}
	/**
	 * 指定添加SQLid的添加
	 * @param mapperStr
	 * @param o 
	 * @return
	 */
	public int insert(final String mapperStr, Object o){
		try {
			return getSqlSession().insert(mapperStr, o);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	/**
	 * 批量添加 
	 * 由于使用Mybatis的批处理接口时将会创建一个新的数据库连接
	 * 该链接不受Spring控制，将导致Spring控制失效，他无法利用当前线程绑定的session
	 * @param entityClass
	 * @param oList
	 * @return
	 */
	public int insert(Class<?> entityClass,final List<?> oList) {
		if(UtilValidate.isEmpty(oList)){
			return 0;
		}
		final String maperStr=entityClass.getName() + POSTFIX_INSERT;
		int result=0;
		for(Object o : oList){
			result+=this.insert(maperStr,o);
			 
		}
		return result;
	}
	
	/**
	 * 默认批量添加
	 * @param oList
	 * @return
	 */
	public int insert(final List<?> oList){
		if(UtilValidate.isEmpty(oList)){
			return 0;
		}
		int result=0;
		for(Object o : oList){
			result+=this.insert(o);
			 
		}
		return result;
	}
	
	/**
	 * 指定SQLID的批量添加
	 * @param mapperStr
	 * @param oList
	 * @return
	 */
	public int insert(final String mapperStr,final List<?> oList){
		if(UtilValidate.isEmpty(oList)){
			return 0;
		}
		int result = 0;
		for(Object o : oList){
			result+=this.insert(mapperStr, o);
			 
		}
		return result;
	}
	/**
	 * 保存
	 * 默认执行ID为update的保存SQL
	 * @param o
	 * @return
	 */
	public int update(Object o) {
		try {
			return getSqlSession().update(o.getClass().getName() + POSTFIX_UPDATE, o);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	
	/**
	 * 指定SQLID保存
	 * 可用于动态保存
	 * @param mapperStr SQLID
	 * @param o
	 * @return
	 */
	public int update(String mapperStr, Object o){
		try {
			return getSqlSession().update(mapperStr, o);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	/**
	 * 指定SQLID的删除
	 * @param mapperStr
	 * @param o
	 * @return 受到影响的行数
	 */
	public int delete(final String mapperStr, Object o){
		try {
			return getSqlSession().delete(mapperStr, o);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	
	/**
	 * 无条件删除
	 * 会删除查询到的全部请谨慎使用
	 * @param mapperStr
	 * @return
	 */
	public int delete(String mapperStr){
		try {
			return getSqlSession().delete(mapperStr);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	/**
	 * 删除，默认使用ID为delete的删除
	 * @param o
	 * @return
	 */
	public int remove(Object o) {
		try {
			return getSqlSession().delete(o.getClass().getName() + POSTFIX_DELETE, o);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	/**
	 * 默认批量删除
	 * 调用SQLID为：.delete
	 * @param oList
	 * @return
	 */
	public int remove(final List<?> oList) {
		if(UtilValidate.isEmpty(oList)){
			return 0;
		}
		int result=0;
		for(Object o : oList){
			result+=this.remove(o);
		}
		return result;
	}
	/**
	 * 批量删除
	 * SQLID为.delete
	 * 批量调用单条删除，SQL语句无需考虑条件迭代
	 * @param entityClass
	 * @param oList
	 * @return 返回收到影响的行数
	 */
	public int remove(Class<?> entityClass,final List<?> oList) {
		if(UtilValidate.isEmpty(oList)){
			return 0;
		}
		int result=0;
		final String mapperStr=entityClass.getName() + POSTFIX_DELETE;
		for(Object o : oList){
			try {
				result+=getSqlSession().delete(mapperStr, o);
			} catch (Exception e) {
				logger.error("{}",e);
				throw new SQLExecuteException(e);
			}
		}
		return result;
	}
	
	/**
	 * 指定SQLID的批量删除
	 * 批量调用单条删除，SQL语句无需考虑条件迭代
	 * @param mapperStr
	 * @param oList
	 * @return
	 */
	public int remove(String mapperStr ,final List<?> oList) {
		if(UtilValidate.isEmpty(oList)){
			return 0;
		}
		int result=0;
		for(Object o : oList){
			try {
				result+=getSqlSession().delete(mapperStr, o);
			} catch (Exception e) {
				logger.error("{}",e);
				throw new SQLExecuteException(e);
			}
		}
		return result;
	}
	/**
	 * 根据ID删除对象
	 * 指定ID为deleteByPrimaryKey的删除，无需指定SQLID
	 * @param entityClass
	 * @param id
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public int removeById(Class entityClass, Serializable id) {
		try {
			return getSqlSession().delete(entityClass.getName() + POSTFIX_DELETE_PRIAMARYKEY ,id);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	
	/**
	 * 指定删除方法删除
	 * @param mapperStr
	 * @param o
	 * @return
	 */
	public int remove(String mapperStr, Object o){
		try {
			return getSqlSession().delete(mapperStr, o);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	/**
	 * 返回指定条数的集合
	 * 使用该方法时offset的值不能大于结果集的最大条数
	 * @param entityClass
	 * @param map 查询条件如果为空则查询表中全部数据
	 * @param offset 开始位置
	 * @param limit 返回的结果条数
	 * @return
	 */
	public <T> List<T> find(Class<T> entityClass, Map<String,Object> map ,final int offset ,final int limit) {
		RowBounds bounds = new RowBounds(offset, limit);
		if (UtilValidate.isNotEmpty(map)){
			map.put("findBy", "True");
		}
		
		try {
			return getSqlSession().selectList(entityClass.getName() + POSTFIX_SELECTMAP, map, bounds);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	
	/**
	 * 单表多条件查询
	 * 该方法返回结果集的全部结果
	 * @param entityClass 查询SQL所对应的domain类
	 * @param map 查询条件，如果为空则返回表中全部的记录
	 * @return
	 */
	public <T> List<T> find(Class<T> entityClass, Map<String,Object> map){
		try {
			return getSqlSession().selectList(entityClass.getName() + POSTFIX_SELECTMAP, map);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	/**
	 * 返回一个对象，适用于单字段的查询
	 * @param mapperStr
	 * @return
	 */
	public Object find(String mapperStr){
		try {
			return getSqlSession().selectOne(mapperStr);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	
	/**
	 * sql 查询.
	 * 
	 * @param entityClass 
	 * @param sql 直接sql的语句(需要防止注入式攻击)如果SQL为空则执行.select查询
	 * @return
	 */
	public <T> List<T> find(Class<T> entityClass, String sql) {
		if(UtilValidate.isNotEmpty(sql)){
			try {
				return getSqlSession().selectList(entityClass.getName()+ POSTFIX_SELECTSQL, sql);
			} catch (Exception e) {
				logger.error("{}",e);
				throw new SQLExecuteException(e);
			}
		}else{
			logger.info("查询SQL为空，将使用.select查询");
			return this.find(entityClass);
		}
	}
	
	/**
	 * 查询表中的全部记录
	 * @param entityClass
	 * @return
	 */
	public <T> List<T> find(Class<T> entityClass){
		try {
			return getSqlSession().selectList(entityClass.getName() + POSTFIX_SELECT);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	/**
	 * 指定SQLID 的sql 查询.
	 * @param mapperStr
	 * @param sql 需要查询的SQL，非空
	 * @return
	 */
	public List<Object> find(String mapperStr, String sql) {
		Assert.hasText(sql,"");
		try {
			return getSqlSession().selectList(mapperStr, sql);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	
	/**
	 * 根据属性名和属性值查询对象.
	 * @param entityClass
	 * @param name 属性名，该值不能为空
	 * @param value
	 * @return
	 */
	public <T> List<T> findBy(Class<T> entityClass, String name, Object value) {
		Assert.hasText(name,"");
		Map<String,Object> map = new HashMap<String,Object>();
		map.put(name, value);
		return find(entityClass, map);
	}
	/**
	 * 根据属性名和属性值查询对象.
	 * 使用该方法需确认结果集中的结果唯一
	 * @param entityClass
	 * @param name 属性名称，该名称必须为entityClass 中的某一个属性
	 * @param value
	 * @return 符合条件的唯一对象
	 */
	@SuppressWarnings("unchecked")
	public <T> T findUniqueBy(Class<T> entityClass, String name, Object value) {
		Assert.hasText(name,"");
		Map<String,Object> map = new HashMap<String,Object>();
		try {
			PropertyUtils.getProperty(entityClass.newInstance(), name);
			map.put(name, value);
			map.put("findUniqueBy", "True");
			return (T) getSqlSession().selectOne(entityClass.getName() + POSTFIX_SELECTMAP, map);
		} catch (Exception e) {
			logger.error("Error when propertie on entity:"+ e.getMessage(), e.getCause());
			throw new SQLExecuteException(e);
		}
	}
	
	/**
	 * 根据属性名和属性值以Like AnyWhere方式查询对象.
	 * @param entityClass
	 * @param name
	 * @param value
	 * @return
	 */
	public <T> List<T> findByLike(Class<T> entityClass , String name, String value) {
		Assert.hasText(name,"");
		Map<String,Object> map = new HashMap<String,Object>();
		map.put(name, value);
		map.put("findLikeBy", "True");
		try {
			return getSqlSession().selectList(entityClass.getName() + POSTFIX_SELECTMAP, map);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
		
	}
	
	/**
	 * 返回一页数据
	 * @param mapperStr
	 * @param parameterObject 参数
	 * @param startIndex 开始位置
	 * @param pageSize 页大小
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public Page queryPage(String mapperStr, Map<String, Object> parameterObject, int startIndex, int pageSize){
		/**
		 * RwoBounds的参数
		 * 考虑不同的数据库分页查询不同，具体实际的分页SQL语句查询数据在各自数据库方言中处理，注意前一页和后一页的衔接
		 * offset：略过指定数量的记录相当于startIndex
		 * limit:返回结果的数量相当于pageSize
		 */
		List list;
		long totalCount;
		list = this.queryForList(mapperStr, parameterObject,new RowBounds(startIndex, pageSize));
		try {
			totalCount = (Long) this.find(mapperStr+POSTFIX_STA_COUNT, parameterObject);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
		Page page = new Page(startIndex,totalCount,pageSize,list);
		return page;
		
	}
	/**
	 * 获得指定条数的结果集，分页查询的基础方法
	 * @param mapperStr
	 * @param parameterObject
	 * @param rowBounds
	 * @return
	 */
	private List queryForList(String mapperStr,Map<String, Object> parameterObject, RowBounds rowBounds) {
		try {
			return getSqlSession().selectList(mapperStr, parameterObject, rowBounds);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
		
	}
	/**
	 * 
	 * @param mapperStr
	 * @param parameterObject
	 * @return
	 */
	private Object find(String mapperStr, Object parameterObject) {
		try {
			return getSqlSession().selectOne(mapperStr, parameterObject);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	/**
	 * 可指定查询的分页查询
	 * @param mapperStr
	 * @param parameterObject
	 * @param pageNo 第几页 注意从1开始
	 * @param pageSize 页大小
	 * @return
	 */
	public Page pagedQuery(String mapperStr, Map<String, Object> parameterObject, int pageNo, int pageSize){
		Assert.isTrue(pageNo >= 1, "pageNo should start from 1");
		Long totalCount =  (Long) find(mapperStr +POSTFIX_STA_COUNT, parameterObject);
		Assert.notNull(totalCount,"totalCount Error");
		int offset = pageNo == 1 ? 0 : (pageNo - 1)* pageSize;
		int startIndex = 0,totalPageCount = 0;
		List list = null;
		if (totalCount.longValue() == 0) {
			return new Page();
		}
		
		if (pageSize > 0) {
			// 计算页数
			totalPageCount = (int) (totalCount / pageSize);
			totalPageCount += ((totalCount % pageSize) > 0) ? 1 : 0;

			// 计算skip数量
			if (totalPageCount > pageNo) {
				startIndex = (pageNo - 1) * pageSize;
			} else {
				startIndex = (totalPageCount - 1) * pageSize;
			}
			list = this.queryForList(mapperStr, parameterObject, new RowBounds(offset, pageSize));
		}else{
			list = this.queryForList(mapperStr, parameterObject);
		}
		return new Page(startIndex, totalCount, pageSize, list);
	}
	
	/**
	 * 默认查询的分页查询
	 * 该查询使用默认查询即.select的查询获得分页数据
	 * @param entityClass
	 * @param parameterObject 查询对象，
	 * @param pageNo 起始页码，注意从1开始
	 * @param pageSize 每页数据条数
	 * @return
	 */
	public <T> Page<T> pagedQuery(Class<T> entityClass, Object parameterObject,
			int pageNo, int pageSize) {
		Assert.isTrue(pageNo >= 1, "pageNo should start from 1");
		Long totalCount =  (Long) find(entityClass.getName() + POSTFIX_STA_COUNT, parameterObject);
		Assert.notNull(totalCount,"totalCount Error");
		int offset = pageNo == 1 ? 0 : (pageNo - 1)* pageSize;
		int startIndex = 0,totalPageCount = 0;
		List<T> list;
		if (totalCount.longValue() == 0) {
			return new Page<T>();
		}
		
		if (pageSize > 0) {
			// 计算页数
			totalPageCount = (int) (totalCount / pageSize);
			totalPageCount += ((totalCount % pageSize) > 0) ? 1 : 0;

			// 计算skip数量
			if (totalPageCount > pageNo) {
				startIndex = (pageNo - 1) * pageSize;
			} else {
				startIndex = (totalPageCount - 1) * pageSize;
			}
			try {
				list = getSqlSession().selectList(entityClass.getName() + POSTFIX_SELECT, parameterObject,new RowBounds(offset, pageSize));
			} catch (Exception e) {
				logger.error("{}",e);
				throw new SQLExecuteException(e);
			}
		}else{
			try {
				list = getSqlSession().selectList(entityClass.getName() + POSTFIX_SELECT, parameterObject);
			} catch (Exception e) {
				logger.error("{}",e);
				throw new SQLExecuteException(e);
			}
		}
		return new Page<T>(startIndex, totalPageCount, pageSize, list);
	}
	
	/**
	 * 返回一页数据
	 * @param statementName
	 * @param parameterObject 参数
	 * @param startIndex 开始位置
	 * @param pageSize 页大小
	 * @return
	 */
	public <T> Page<T> queryPage(String statementName, Object parameterObject,int startIndex, int pageSize) {
		Long totalCount =  (Long) find(statementName + POSTFIX_STA_COUNT, parameterObject);
		Assert.notNull(totalCount,"totalCount Error");
		if (totalCount.longValue() == 0) {
			return new Page<T>();
		}
		List<T> list;
		// 如果pageSize小于0,则返回所有数据,等同于getAll
		try {
			if (pageSize > 0) {
				list = getSqlSession().selectList(statementName , parameterObject,new RowBounds(startIndex, pageSize));
			}else{
				list = getSqlSession().selectList(statementName, parameterObject);
			}
		}catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
		return new Page<T>(startIndex, totalCount, pageSize, list);
	}
	/**
	 * 根据条件获得
	 * @param mapperStr
	 * @param parameterObject
	 * @return
	 */
	public   Object queryForObject(String mapperStr, Object parameterObject){
		try {
			return getSqlSession().selectOne(mapperStr, parameterObject);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	/**
	 * 指定SQL语句获得一个对象，
	 * 使用此方法之前必须确保此次查询的结果为1
	 * @param mapperStr
	 * @return
	 */
	public   Object queryForObject(String mapperStr){
		try {
			return getSqlSession().selectOne(mapperStr, null);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	/**
	 * 
	 * @param mapperStr
	 * @return
	 */
	public List<?> queryForList(String mapperStr){
		try {
			return getSqlSession().selectList(mapperStr);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	
	/**
	 * 
	 * @param mapperStr
	 * @param skipResults
	 * @param maxResults
	 * @return
	 */
	public List<?> queryForList(String mapperStr, int skipResults, int maxResults){

		return queryForList(mapperStr, null, skipResults, maxResults);
	}
	/**
	 * 获得指定条数的查询结果，
	 * 注意：如果调用此方法则不再进行物理分页的拦截，查询时如果结果的数据量较大建议使用queryForList(final String mapperStr, final Object parameterObject)
	 * 此方法是否也修改为物理分页的查询，如果需要则计算页的大小，用maxResults - skipResults
	 * @param mapperStr
	 * @param parameterObject
	 * @param skipResults 查询的开始位置
	 * @param maxResults 查询条数的结束位置
	 * @return
	 */
	public List<?> queryForList(final String mapperStr, final Object parameterObject, final int skipResults, final int maxResults){
		RowBounds bounds = new RowBounds(skipResults, maxResults);
		try {
			return getSqlSession().selectList(mapperStr, parameterObject, bounds);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	/**
	 * 
	 * @param mapperStr
	 * @param parameterObject
	 * @return
	 */
	public List<?> queryForList(final String mapperStr, final Object parameterObject) {
		try {
			return getSqlSession().selectList(mapperStr, parameterObject);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	
	/**
	 * 获得Map形式的结果集，结果集作为Map的value，
	 * @param mapperStr
	 * @param parameterObject
	 * @param keyProperty 结果集在Map中对应的key
	 */
	public Map<String, Object> queryForMap(final String mapperStr, final Object parameterObject, final String keyProperty){
		try {
			return getSqlSession().selectMap(mapperStr, parameterObject, keyProperty);
		} catch (Exception e) {
			logger.error("{}",e);
			throw new SQLExecuteException(e);
		}
	}
	

}
